//@author A0097005J
package logic;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;

import sharedUtils.Constants;
import storage.Task;
import storage.TaskList;

/**
 * Notes: Date format is ddMMyy e.g 031014, 3rd of OCT, 2014 All year are
 * assumed to be 2000+ onwards, thus the range is from 2000 to 2099
 */
public class InputDecoder {

	private static final int INVALID_CODE = 0;
	private static final int ADD_CODE = 1;
	private static final int DELETE_CODE = 2;
	private static final int SEARCH_CODE = 3;
	private static final int EDIT_CODE = 4;
	private static final int UNDO_CODE = 5;
	private static final int REDO_CODE = 6;
	private static final int DISPLAY_CODE = 7;
	private static final int TODAY_CODE = 8;
	private static final int DAY_CODE = 9;
	private static final int WEEK_CODE = 10;
	private static final int MONTH_CODE = 11;
	
	private static final int ADD_INVALID_CODE = -1;
	private static final int DELETE_INVALID_CODE = -2;
	private static final int SEARCH_INVALID_CODE = -3;
	private static final int EDIT_INVALID_CODE = -4;
	private static final int UNDO_INVALID_CODE = -5;
	private static final int REDO_INVALID_CODE = -6;

	// Corresponding string for each command
	// Possible to switch to String[] for various alias for a single command.
	// Basic command implementation for now.
	private static final String[] ADD_COMMAND_STRING = {"add", "a"};
	private static final String[] DELETE_COMMAND_STRING = {"del", "d", "delete"};
	private static final String[] SEARCH_COMMAND_STRING = {"search", "s"};
	private static final String[] EDIT_COMMAND_STRING = {"edit", "e"};
	private static final String[] UNDO_COMMAND_STRING = {"undo", "u"};
	private static final String[] REDO_COMMAND_STRING = {"redo", "r"};
	private static final String[] DISPLAY_COMMAND_STRING = {"display", "all"};
	
	private static final String[] TODAY_COMMAND_STRING = {"today"};
	private static final String[] DAY_COMMAND_STRING = {"day"};
	private static final String[] WEEK_COMMAND_STRING = {"week"};
	private static final String[] MONTH_COMMAND_STRING = {"month"};


	private static final String DATE_KEYFIELD = "`date";
	// private static final String LOCATION_KEYFIELD = "`loc";
	private static final String RANGE_KEYFIELD = "`to";
	private static final String DESCRIPTION_KEYFIELD = "`desc";
	// private static final String TYPE_KEYFIELD = "`type";
	// private static final String TITLE_KEYFIELD = "`title"; // unused
	//private static final String TASKID_KEYFIELD = "`id"; // unused
	private static final String PRIORITY_KEYFIELD = "!";
	//private static final String REMINDER_KEYFIELD = "`remind";
	private static final String FREQUENCY_KEYFIELD = "`freq";
	private static final String CLEAR_KEYFIELD = "clear";

	private static final int INVALID_FIELD_CODE = -1;
	private static final int TITLE_CODE = -1;
	private static final int DATE_CODE = 0;
	// private static final int LOCATION_CODE = 1;
	private static final int RANGE_CODE = 2;
	private static final int DESCRIPTION_CODE = 3;
	// private static final int TYPE_CODE = 4;
	//private static final int TASKID_CODE = 5; // unused
	private static final int PRIORITY_CODE = 6;
	//private static final int REMINDER_CODE = 7;
	private static final int FREQUENCY_CODE = 8;

	private static final int COMMAND_START_POS = 0;
	private static final int FIELD_START_POS = 1;
	private static final int PRIORITY_START_POS = 1;

	private static final int DEFAULT_HOUR_OF_DAY = 23;
	private static final int DEFAULT_MINUTES = 59;
	private static final int DEFAULT_SECONDS = 0;

	/*
	 * parameters format - parameter[0] = command, parameter[1...] = the data
	 * title follows command immediately till a special marker/keyfield is read
	 */

	public static boolean isIntegerPositive(String str) {
		return isIntegerPositive(str, 10);
	}

	public static boolean isIntegerPositive(String str, int radix) {
		if (str.isEmpty())
			return false;
		for (int i = 0; i < str.length(); i++) {
			if (Character.digit(str.charAt(i), radix) < 0)
				return false;
		}
		return true;
	}

	/**
	 * Called when adding a task to TaskList
	 * @param parameters raw input String
	 * @param taskList taskList to add the new task to
	 * @return DisplayPackage containing the added task and ADD_CODE if successful. Returns ADD_INVALID_CODE if failed.
	 */
	private DisplayPackage add(String[] parameters, TaskList taskList) {
		if (parameters.length <= 1) {
			return new DisplayPackage(ADD_INVALID_CODE);
		}
		Task task = create_Task(parameters);
		if (taskList.add(task) != 0) {
			return new DisplayPackage(ADD_INVALID_CODE);
		}
		return new DisplayPackage(ADD_CODE, task); // assuming its done
	}

	/**
	 * Compare the String cmd to the String[] list. If cmd matches any String in list
	 * @param list List of Strings to be compared
	 * @param cmd String to be compared
	 * @return 0 if cmd matches any String, -1 if no match is found.
	 */
	private int compareToArray(String[] list, String cmd) {
		if(list == null || cmd == null) {
			return -1;
		}
		
		for(int i=0; i<list.length; i++) {
			if(list[i].compareToIgnoreCase(cmd) == 0) {
				return Constants.SUCCESS_ERROR_CODE;
			}
		}
		return -1;
	}

	/**
	 * Generates a task given the input parameters.
	 * @param parameters String[] to generate the task from.
	 * @return Generated task. null if title does not exist.
	 */
	public Task create_Task(String[] parameters) {
		Task task = new Task();

		String title = extract_field(parameters, TITLE_CODE);
		if (title == null) {
			return null;
		}
		String priority = extract_field(parameters, PRIORITY_CODE);
		String description = extract_field(parameters, DESCRIPTION_CODE);
		// String location = decode_field(parameters, LOCATION_CODE);
		// String type = decode_field(parameters, TYPE_CODE);

		//Start to extract dates and create startDate and endDate
		GregorianCalendar startDate = null;
		GregorianCalendar endDate = null;
		SimpleDateFormat simpleTemp;

		boolean isEndRange = false;
		boolean isStartDateSet = false;
		boolean isStartTimeSet = false;
		boolean isEndDateSet = false;
		boolean isEndTimeSet = false;

		boolean clear_StartDate = false;
		boolean clear_EndDate = false;
		String date_string = extract_field(parameters, DATE_CODE);
		if (date_string != null) {
			String date_split[] = date_string.split(" ");
			for (int i = 0; i < date_split.length; i++) {
				Date tempDate;
				date_split[i] = format_date(date_split[i]);
				if (date_split[i].compareTo(RANGE_KEYFIELD) == 0) {
					isEndRange = true;
				} else if (date_split[i].length() == 6
						|| date_split[i].length() == 8) {
					if (isEndRange == false && isStartDateSet == false) {
						if (startDate == null) {
							startDate = new GregorianCalendar();
							init_DayMonthYear(startDate);
						}
						if ((tempDate = process_Date(date_split[i])) != null) {
							set_DayMonthYear(startDate, tempDate);
							isStartDateSet = true;
						}
					} else if (isEndRange == true && isEndDateSet == false) {
						if (endDate == null) {
							endDate = new GregorianCalendar();
							init_DayMonthYear(endDate);
						}
						if ((tempDate = process_Date(date_split[i])) != null) {
							set_DayMonthYear(endDate, tempDate);
							isEndDateSet = true;
						}
					}
				} else if (date_split[i].length() == 4) {
					if (isEndRange == false && isStartTimeSet == false) {
						if (startDate == null) {
							startDate = new GregorianCalendar();
						}
						if ((tempDate = process_Time(date_split[i])) != null) {
							set_HourMin(startDate, tempDate);
							isStartTimeSet = true;
						}
					} else if (isEndRange == true && isEndTimeSet == false) {
						if (endDate == null) {
							endDate = new GregorianCalendar();
						}
						if ((tempDate = process_Time(date_split[i])) != null) {
							set_HourMin(endDate, tempDate);
							if (isStartDateSet == true && isEndDateSet == false) {
								set_DayMonthYear(endDate, startDate.getTime());
							}
							isEndTimeSet = true;
						}
					}
				}
			}
		}

		//Starts to set the empty task to its proper fields
		task.setTitle(title);
		task.setDescription(description);
		if (priority != null) {
			assert (priority.compareTo(PRIORITY_KEYFIELD) == 0);
			task.setPriority(Constants.HIGH_PRIORITY);
		} else {
			task.setPriority(Constants.LOW_PRIORITY);
		}
		// Swap end and start date if start is after date
		/*
		 * if(startDate != null && endDate != null) {
		 * if(startDate.after(endDate)) { GregorianCalendar temp = startDate;
		 * startDate = endDate; endDate = temp; } }
		 */
		task.setStartDate(startDate);
		task.setEndDate(endDate);
		task.setStartDateSet(isStartDateSet);
		task.setStartTimeSet(isStartTimeSet);
		task.setEndDateSet(isEndDateSet);
		task.setEndTimeSet(isEndTimeSet);
		return task;
	}

	/**
	 * Decodes input and return the command type Assumption: first parameter is
	 * always a command e.g "add asda". Anything else, it is invalid.
	 * 
	 * @param input
	 * @return Command Code, refer to declared constants	
	 */
	public int decode_command(String[] parameters) {
		String cmd;
		if (parameters == null) {
			return -1;
		} else {
			cmd = parameters[COMMAND_START_POS];
		}
		if (compareToArray(ADD_COMMAND_STRING, cmd) == 0) {
			return ADD_CODE;
		} else if (compareToArray(DELETE_COMMAND_STRING, cmd) == 0) {
			return DELETE_CODE;
		} else if (compareToArray(SEARCH_COMMAND_STRING, cmd) == 0) {
			return SEARCH_CODE;
		} else if (compareToArray(EDIT_COMMAND_STRING, cmd) == 0) {
			return EDIT_CODE;
		} else if (compareToArray(UNDO_COMMAND_STRING, cmd) == 0) {
			return UNDO_CODE;
		} else if (compareToArray(REDO_COMMAND_STRING, cmd) == 0) {
			return REDO_CODE;
		} else if (compareToArray(DISPLAY_COMMAND_STRING, cmd) == 0) {
			return DISPLAY_CODE;
		} else if (compareToArray(DAY_COMMAND_STRING, cmd) == 0) {
			return DAY_CODE;
		} else if (compareToArray(TODAY_COMMAND_STRING, cmd) == 0) {
			return TODAY_CODE;
		} else if (compareToArray(WEEK_COMMAND_STRING, cmd) == 0) {
			return WEEK_CODE;
		} else if (compareToArray(MONTH_COMMAND_STRING, cmd) == 0) {
			return MONTH_CODE;
		} else {
			return INVALID_CODE;
		}
	}

	/**
	 * Delete function, to delete the task specified by parameters.
	 * @param parameters Raw command String input
	 * @param taskList TaskList where the task is to be deleted
	 * @return Deleted Task with DELETE_CODE if successful, DELETE_INVALID_CODE if failed.
	 */
	private DisplayPackage delete(String[] parameters, TaskList taskList) {
		if (parameters.length <= 1) {
			return new DisplayPackage(DELETE_INVALID_CODE);
		}
		String title = extract_field(parameters, TITLE_CODE);
		Task task;
		if (title != null) {
			if (isIntegerPositive(title) == true) {
				task = taskList.delete(Integer.parseInt(title));
				if (task == null) {
					return new DisplayPackage(DELETE_INVALID_CODE);
				} else {
					return new DisplayPackage(DELETE_CODE, task);
				}
			} else {
				if ((task = taskList.delete(title)) == null) {
					return new DisplayPackage(DELETE_INVALID_CODE);
				} else {
					return new DisplayPackage(DELETE_CODE, task);
				}
			}
		} else {
			return new DisplayPackage(DELETE_INVALID_CODE);
		}
	}

	public DisplayPackage display(TaskList taskList) {
		return new DisplayPackage(DISPLAY_CODE, taskList.getAll());
	}
	
	/**
	 * Function to return a display package containing all task with the specified day(s)
	 * @param parameters	The raw input string
	 * @param taskList		TaskList used by the program
	 * @return				DisplayPackage containing all Task within specified day(s)
	 */
	private DisplayPackage display_day(String[] parameters, TaskList taskList) {
		if(taskList == null) {
			return null;
		}
		
		String title = extract_field(parameters, TITLE_CODE);
		int number = -1;
		if(title != null && isIntegerPositive(title) == true) {
			return new DisplayPackage(DISPLAY_CODE, taskList.getAllByDay(Integer.parseInt(title)));
		} else {
			return new DisplayPackage(DISPLAY_CODE, taskList.getAllByDay()); 
		}
	}

	/**
	 * Function to return a display package containing all task with the specified month(s)
	 * @param parameters	The raw input string
	 * @param taskList		TaskList used by the program
	 * @return				DisplayPackage containing all Task within specified month(s)
	 */
	private DisplayPackage display_month(String[] parameters, TaskList taskList) {
		if(taskList == null) {
			return null;
		}
		
		String title = extract_field(parameters, TITLE_CODE);
		int number = -1;
		if(title != null && isIntegerPositive(title) == true) {
			return new DisplayPackage(DISPLAY_CODE, taskList.getAllByMonth(Integer.parseInt(title)));
		} else {
			return new DisplayPackage(DISPLAY_CODE, taskList.getAllByMonth()); 
		}
	}

	/**
	 * Function to return a display package containing all task occurring today
	 * @param parameters	The raw input string
	 * @param taskList		TaskList used by the program
	 * @return				DisplayPackage containing all Task occurring today
	 */
	private DisplayPackage display_today(TaskList taskList) {
		if(taskList == null) {
			return null;
		}
		
		return new DisplayPackage(DISPLAY_CODE, taskList.getAllToday());
	}
	
	/**
	 * Function to return a display package containing all task with the specified week(s)
	 * @param parameters The raw input string
	 * @param taskList TaskList used by the program
	 * @return DisplayPackage containing all Task within specified week(s)
	 */
	private DisplayPackage display_week(String[] parameters, TaskList taskList) {
		if(taskList == null) {
			return null;
		}
		
		String title = extract_field(parameters, TITLE_CODE);
		int number = -1;
		if(title != null && isIntegerPositive(title) == true) {
			return new DisplayPackage(DISPLAY_CODE, taskList.getAllByWeek(Integer.parseInt(title)));
		} else {
			return new DisplayPackage(DISPLAY_CODE, taskList.getAllByWeek()); 
		}
	}

	/**
	 * Edits a task, Start by extracting fields from parameters and replace non null fields into the Task that is edited
	 * @param parameters The raw input string
	 * @param taskList TaskList used by the program
	 * @return DisplayPackage containing Task being edited
	 */
	private DisplayPackage edit(String[] parameters, TaskList taskList) {
		if (parameters.length <= 1) {
			return new DisplayPackage(EDIT_INVALID_CODE);
		}
		String title = extract_field(parameters, TITLE_CODE);
		String priority = extract_field(parameters, PRIORITY_CODE);
		String dateField = extract_field(parameters, DATE_CODE);

		ArrayList<Task> tasks = null;
		Task task;
		if (title == null) {
			return new DisplayPackage(EDIT_INVALID_CODE);
		}
		if (title.split(" ").length == 1) {
			if (isIntegerPositive(title) == true) {
				int taskID = Integer.parseInt(title);
				tasks = taskList.searchForTaskId(taskID);
			} else {
				tasks = taskList.searchForTitle(title);
			}
		} else {
			tasks = taskList.searchForTitle(title);
		}

		if (tasks == null || tasks.size() == 0) {
			return new DisplayPackage(EDIT_INVALID_CODE);
		} else {
			task = tasks.get(0);
		}

		// Begin Recreating task
		Task tempTask = create_Task(parameters);
		task = Task.createCopyFrom(task);

		// Being check if dates are to be cleared
		boolean clear_StartDate = false;
		boolean clear_EndDate = false;
		if (dateField != null) {
			boolean endRange = false;
			String[] date_split = dateField.split(" ");
			for (int i = 0; i < date_split.length; i++) {
				if (date_split[i].compareTo(RANGE_KEYFIELD) == 0) {
					endRange = true;
				} else if (date_split[i].compareTo(CLEAR_KEYFIELD) == 0) {
					if (endRange == false) {
						clear_StartDate = true;
					} else if (endRange == true) {
						clear_EndDate = true;
					}
				}
			}
		}

		// Begin setting task
		if (priority != null) {
			task.setPriority(Constants.HIGH_PRIORITY);
		} else {
			task.setPriority(Constants.LOW_PRIORITY);
		}
		if (tempTask.getDescription() != null) {
			task.setDescription(tempTask.getDescription());
		}
		if(clear_StartDate == true) {
			task.setStartDate(null);
			task.setStartDateSet(false);
			task.setStartTimeSet(false);
		} else if (tempTask.getStartDate() != null) {
			task.setStartDate(tempTask.getStartDate());
			task.setStartDateSet(tempTask.isStartDateSet());
			task.setStartTimeSet(tempTask.isStartTimeSet());
		}
		
		if(clear_EndDate == true) {
			task.setEndDate(null);
			task.setEndDateSet(false);
			task.setEndTimeSet(false);
		} else if (tempTask.getEndDate() != null) {
			task.setEndDate(tempTask.getEndDate());
			task.setEndDateSet(tempTask.isEndDateSet());
			task.setEndTimeSet(tempTask.isEndTimeSet());
		}
		if (taskList.update(task.getId(), task) == 0) {
			return new DisplayPackage(EDIT_CODE, task);
		} else {
			return new DisplayPackage(EDIT_INVALID_CODE);
		}
	}

	/**
	 * Execute command base on command code of the first word
	 * @param input Raw input String
	 * @param taskList TaskList to be used
	 * @return
	 */
	public DisplayPackage exec_command(String input, TaskList taskList) {
		String parameters[];
		parameters = input.split(" ");
		int command_code = decode_command(parameters);

		switch (command_code) {
			case ADD_CODE : {
				return add(parameters, taskList);
			}
			case DELETE_CODE : {
				return delete(parameters, taskList);
			}
			case SEARCH_CODE : {
				return search(parameters, taskList);
			}
			case EDIT_CODE : {
				return edit(parameters, taskList);
			}
			case UNDO_CODE : {
				return undo(taskList);
			}
			case REDO_CODE : {
				return redo(taskList);
			}
			case DISPLAY_CODE : {
				return display(taskList);
			}
			case TODAY_CODE : {
				return display_today(taskList);
			}
			case DAY_CODE : {
				return display_day(parameters, taskList);
			}
			case WEEK_CODE : {
				return display_week(parameters, taskList);
			}
			case MONTH_CODE : {
				return display_month(parameters, taskList);
			}
			default : {
				return new DisplayPackage(INVALID_CODE);
			}
		}
	}

	/**
	 * Extract fields after corresponding key fields such as everything after `desc, until it reads another keyfield.
	 * `date will continue to be read if it encounters `to
	 * ! will only count as priority if it is the 2nd word.
	 * input string must at least contain a field to decode. "add" - invalid
	 * "add abc" - valid "add !" - valid
	 * @param parameters Raw String input
	 * @param keyCode Which key field to extract.
	 * @return Extracted String
	 */
	public String extract_field(String[] parameters, int keyCode) {
		if (parameters == null) {
			return null;
		} else if (parameters.length < 2) {
			return null;
		}
		int i = FIELD_START_POS;
		int startPos;
		if (keyCode != TITLE_CODE) {
			boolean found = false;
			while (found == false && i < parameters.length) {
				if (get_keyFieldCode(parameters[i]) != keyCode) {
					i++;
				} else {
					found = true;
				}
			}
		}
		if (i >= parameters.length) {
			return null;
		}
		startPos = i;
		if (parameters[startPos].compareTo(PRIORITY_KEYFIELD) == 0
				&& keyCode == PRIORITY_CODE) {
			if (startPos != PRIORITY_START_POS) {
				return null;
			} else {
				return parameters[startPos];
			}
		} else if (parameters[startPos].compareTo(PRIORITY_KEYFIELD) == 0
				&& keyCode == TITLE_CODE) {
			startPos++;
		} else if (keyCode != TITLE_CODE) {
			startPos++;
		}

		String field = generate_field(parameters, startPos, keyCode);
		if (field == null) {
			return null;
		} else if (field.compareTo("") != 0) {
			return field;
		} else {
			return null;
		}
	}

	/**
	 * Delimits the String date. Characters delimited: .,/;:
	 * @param date String to be delimited
	 * @return String date without .,/;:
	 */
	private String format_date(String date) {
		String[] split = date.split("[.,/;:]");
		String result = "";
		if (split != null) {
			for (int i = 0; i < split.length; i++) {
				result += split[i];
			}
		} else {
			return null;
		}
		if (result.compareTo("") != 0) {
			return result;
		} else {
			return null;
		}
	}

	/**
	 * Start reading from start_pos in parameters and stop once it reads another KeyCode.
	 * Will continue reading pass `to if keyCode is `date
	 * @param parameters
	 * @param start_pos
	 * @param keyCode
	 * @return
	 */
	private String generate_field(String[] parameters, int start_pos,
			int keyCode) {
		String field = "";
		boolean hasWritten = false;

		for (int i = start_pos; i < parameters.length; i++) {
			if (get_keyFieldCode(parameters[i]) == INVALID_FIELD_CODE) {
				if (parameters[i].compareTo("") != 0) {
					if (hasWritten == true) {
						field += " ";
					}
				}
				field += parameters[i];
				hasWritten = true;
			} else if (keyCode == DATE_CODE
					&& get_keyFieldCode(parameters[i]) == RANGE_CODE) {
				if (hasWritten == true) {
					field += " ";
				}
				field += parameters[i];
				hasWritten = true;
			} else {
				return field;
			}
		}
		return field;
	}
	
	/**
	 * Compare String to key fields and return correct code.
	 * @param field String to be compared
	 * @return keyCode
	 */
	private int get_keyFieldCode(String field) {
		if (field.compareToIgnoreCase(DATE_KEYFIELD) == 0) {
			return DATE_CODE;
		} else if (field.compareToIgnoreCase(RANGE_KEYFIELD) == 0) {
			return RANGE_CODE;
		} else if (field.compareToIgnoreCase(DESCRIPTION_KEYFIELD) == 0) {
			return DESCRIPTION_CODE;
		}/*
		 * else if (field.compareToIgnoreCase(TASKID_KEYFIELD) == 0) { return
		 * TASKID_CODE; }*/
		 else if (field.compareToIgnoreCase(PRIORITY_KEYFIELD) == 0) {
			return PRIORITY_CODE;
		/*} else if (field.compareToIgnoreCase(REMINDER_KEYFIELD) == 0) {
			return REMINDER_CODE;*/
		} else if (field.compareToIgnoreCase(FREQUENCY_KEYFIELD) == 0) {
			return FREQUENCY_CODE;
		}
		return INVALID_FIELD_CODE;
	}

	/**
	 * Initialise the varDate to default values for Hour mins and seconds while leaving day, month and year the same.
	 * @param varDate GregorianCalendar variable to be set.
	 */
	private void init_DayMonthYear(GregorianCalendar varDate) {
		int year = varDate.get(GregorianCalendar.YEAR);
		int month = varDate.get(GregorianCalendar.MONTH);
		int date = varDate.get(GregorianCalendar.DATE);
		varDate.set(year, month, date, DEFAULT_HOUR_OF_DAY, DEFAULT_MINUTES,
				DEFAULT_SECONDS);
	}

	/**
	 * Parse the String date and create a Date object. Accept format ddMMyy and ddMMyyyy
	 * @param date String to be parsed
	 * @return The new Date object, null if parse fails.
	 */
	private Date process_Date(String date) {
		try {
			if (date.length() == 6) {
				SimpleDateFormat df = new SimpleDateFormat("ddMMyy");
				df.setLenient(false);
				return df.parse(date);
			} else if (date.length() == 8) {
				SimpleDateFormat df = new SimpleDateFormat("ddMMyyyy");
				df.setLenient(false);
				return df.parse(date);
			} else {
				return null;
			}
		} catch (ParseException e) {
			return null;
		}
	}

	/**
	 * Parse the String time and create a Date object. Accept format HHmm
	 * @param date String to be parsed
	 * @return The new Date object, null if parse fails.
	 */
	private Date process_Time(String time) {
		try {
			SimpleDateFormat df = new SimpleDateFormat("HHmm");
			df.setLenient(false);
			return df.parse(time);
		} catch (ParseException e) {
			return null;
		}
	}
	private DisplayPackage redo(TaskList taskList) {
		DisplayPackage pack;
		if (taskList.redo() == Constants.SUCCESS_ERROR_CODE) {
			pack = new DisplayPackage(REDO_CODE);
		} else {
			pack = new DisplayPackage(REDO_INVALID_CODE);
		}
		return pack;
	}

	/**
	 * Searches the taskList for task that contains all words in parameters excluding the first word "search".
	 * Searches title and description of a task.
	 * @param parameters Raw input String
	 * @param taskList taskList to be searched
	 * @return Return search results in DisplayPackage.
	 */
	private DisplayPackage search(String[] parameters, TaskList taskList) {
		if (parameters.length <= 1) {
			return new DisplayPackage(SEARCH_INVALID_CODE);
		}
		ArrayList<Task> list = new ArrayList<Task>();
		String words = "";
		String title = extract_field(parameters, TITLE_CODE);
		String description = extract_field(parameters, DESCRIPTION_CODE);
		if (title != null) {
			words += title;
		}
		if (description != null) {
			if (title != null) {
				words += " ";
			}
			words += description;
		}

		if (isIntegerPositive(words)) {
			list = taskList.searchForTaskId(Integer.parseInt(words));
		} else {
			list = taskList.searchFor(words.split(" "));
		}

		if (list == null) {
			return new DisplayPackage(SEARCH_INVALID_CODE);
		}
		if (list.size() == 0) {
			return new DisplayPackage(SEARCH_INVALID_CODE);
		}
		if (list.size() >= 1) {
			return new DisplayPackage(SEARCH_CODE, list);
		} else {
			return new DisplayPackage(SEARCH_INVALID_CODE);
		}
	}

	/**
	 * Set the day, month and year of varDate as the same as currDate
	 * @param varDate
	 * @param currDate
	 */
	private void set_DayMonthYear(GregorianCalendar varDate, Date currDate) {
		GregorianCalendar tempCal = new GregorianCalendar();
		tempCal.setTime(currDate);
		int year = tempCal.get(GregorianCalendar.YEAR);
		int month = tempCal.get(GregorianCalendar.MONTH);
		int date = tempCal.get(GregorianCalendar.DATE);

		varDate.set(year, month, date);
	}

	/**
	 * set the hour and min of currDate to varDate
	 * @param varDate
	 * @param currDate
	 */
	private void set_HourMin(GregorianCalendar varDate, Date currDate) {
		GregorianCalendar tempCal = new GregorianCalendar();
		tempCal.setTime(currDate);
		int year = varDate.get(GregorianCalendar.YEAR);
		int month = varDate.get(GregorianCalendar.MONTH);
		int date = varDate.get(GregorianCalendar.DATE);

		int hourOfDay = tempCal.get(GregorianCalendar.HOUR_OF_DAY);
		int minute = tempCal.get(GregorianCalendar.MINUTE);

		varDate.set(year, month, date, hourOfDay, minute, DEFAULT_SECONDS);
	}

	/**
	 * Undo the previous action
	 * @param taskList taskList, action to be undone.
	 * @return
	 */
	private DisplayPackage undo(TaskList taskList) {
		DisplayPackage pack;
		if (taskList.undo() == Constants.SUCCESS_ERROR_CODE) {
			pack = new DisplayPackage(UNDO_CODE);
		} else {
			pack = new DisplayPackage(UNDO_INVALID_CODE);
		}
		return pack;
	}
}